/*
 * #%L
 * Client
 * %%
 * Copyright (C) 2011 Ractoc.com
 * %%
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * #L%
 */
package com.ractoc.opengamefinder.client.plugins.nifty.screens;

import java.io.IOException;
import org.apache.log4j.Logger;

import com.ractoc.opengamefinder.api.messages.ChatEnterRoomMessage;
import com.ractoc.opengamefinder.api.messages.ChatLeaveRoomMessage;
import com.ractoc.opengamefinder.api.messages.ChatLineMessage;
import com.ractoc.opengamefinder.api.messages.GetAvatarMessage;
import com.ractoc.opengamefinder.api.results.GetAvatarMessageResult;
import com.ractoc.opengamefinder.api.results.MessageResultsErrorCodes;
import com.ractoc.opengamefinder.api.vo.AccountVO;
import com.ractoc.opengamefinder.api.vo.AvatarVO;
import com.ractoc.opengamefinder.client.GuiContainer;
import com.ractoc.opengamefinder.client.plugins.ChatPlugin;
import com.ractoc.opengamefinder.client.plugins.nifty.BaseScreen;
import com.ractoc.opengamefinder.client.plugins.results.DisplayScreenMessageResult;
import com.ractoc.pffj.api.BasePluginMessageResult;
import com.ractoc.pffj.api.PluginMessage;
import com.ractoc.pffj.core.PluginController;
import com.ractoc.pffj.core.PluginException;
import de.lessvoid.nifty.controls.Chat;
import de.lessvoid.nifty.controls.button.builder.ButtonBuilder;
import de.lessvoid.nifty.controls.chatcontrol.ChatControl;
import de.lessvoid.nifty.controls.chatcontrol.ChatEntryModelClass;

import de.lessvoid.nifty.controls.chatcontrol.builder.ChatBuilder;
import de.lessvoid.nifty.elements.Element;
import de.lessvoid.nifty.render.NiftyImage;
import java.io.File;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;

/**
 * Handles any messages sent to the NiftyPlugin of ScreenType CHAT. <p/> <p/>
 * <p/>
 * <p/>
 * @author ractoc
 * @version 0.1
 */
public class ChatScreen extends BaseScreen {

    private static final String ICON_HOURGLASS = "images/hourglass.png";
    private static final String NAME_LINE_SEPERATOR = " > ";
    private static final String CHAT_PANEL = "chatPanel";
    private static final String CHAT = "chat";
    private static Logger logger = Logger.getLogger(ChatScreen.class.getName());
    private ConcurrentHashMap<String, ChatRoom> rooms = new ConcurrentHashMap<String, ChatRoom>();
    private String currentChatPanelId = "";

    /**
     * Default constructor.
     * Registers a locator on the asset manager. This is needed to be able to load the cached files.
     */
    public ChatScreen() {
        try {
            String filePath = new File(".").getCanonicalPath();
            logger.debug("registering locator to path " + filePath);
            GuiContainer.getInstance().getAssetManager().registerLocator(filePath, com.jme3.asset.plugins.FileLocator.class);
        }
        catch (IOException ex) {
            logger.error("unable to register locator for path '.'");
        }
    }

    /**
     * Main processing method. All messages that need to be processed pass
     * through here. Supported message types are:
     * <ul>
     * <li>ChatLineMessage</li>
     * <li>ChatEnterRoomMessage</li>
     * <li>ChatLeaveRoomMessage</li>
     * </ul>
     * `
     * @param msg The message to process.
     * @return The result from processing the message, or null of the message
     *         was unsupported.
     */
    @Override
    public final BasePluginMessageResult screenProcessing(final PluginMessage msg) {

        BasePluginMessageResult result = null;

        if (msg instanceof ChatLineMessage) {
            ChatLineMessage clMsg = (ChatLineMessage) msg;
            logger.debug("adding message to screen");
            result = rooms.get(clMsg.getRoomId()).processChatLine(clMsg);
        } else if (msg instanceof ChatEnterRoomMessage) {
            ChatEnterRoomMessage cerMsg = (ChatEnterRoomMessage) msg;
            if (!rooms.containsKey(cerMsg.getRoomId())) {
                createRoom(cerMsg.getRoomId());
            }
            logger.debug("adding player to the list");
            result = rooms.get(cerMsg.getRoomId()).processEnterRoom(cerMsg);
        } else if (msg instanceof ChatLeaveRoomMessage) {
            ChatLeaveRoomMessage clrMsg = (ChatLeaveRoomMessage) msg;
            logger.debug("removing player from the list");
            result = rooms.get(clrMsg.getRoomId()).processLeaveRoom(clrMsg);
            destroyRoom(clrMsg.getRoomId());
        }

        return result;
    }

    private void createRoom(String roomId) {
        logger.info("creating room " + roomId);
        if (!rooms.isEmpty() && rooms.containsKey(roomId)) {
            throw new IllegalArgumentException("Room already exists.");
        }
        rooms.put(roomId, new ChatRoom());
        final Element chatPanel = nifty.getCurrentScreen().findElementByName(CHAT);
        Element button = new ButtonBuilder(roomId + "-button", roomId).build(nifty, nifty.getCurrentScreen(), chatPanel);
        ChatBuilder chatBuilder = new ChatBuilder(roomId + "-chat", ChatPlugin.getLines());
        chatBuilder.sendLabel("Send");
        Element chat = chatBuilder.build(nifty, nifty.getCurrentScreen(), chatPanel);
        rooms.get(roomId).setChatPanelId(roomId + "-chat");
        chatPanel.findElementByName("chat-tabs").add(button);
        chatPanel.findElementByName("chat-tabs").layoutElements();
        chatPanel.findElementByName("chat-container").add(chat);
        chatPanel.findElementByName("chat-container").layoutElements();
        chatPanel.initControls();
        if (!currentChatPanelId.equals("")) {
            nifty.getCurrentScreen().findElementByName(currentChatPanelId).hide();
        }
        chat.show();
    }

    private void destroyRoom(String roomId) {
        logger.info("destroying room " + roomId);
        if (rooms.isEmpty() || !rooms.containsKey(roomId)) {
            throw new IllegalArgumentException("Room does not exist.");
        }
        final Element chatPanel = nifty.getCurrentScreen().findElementByName(CHAT);
        chatPanel.findElementByName("chat-tabs").findElementByName(roomId + "-button").markForRemoval();
        rooms.remove(roomId);
    }

    private class ChatRoom {

        private String chatPanelId;

        public String getChatPanelId() {
            return chatPanelId;
        }

        public void setChatPanelId(String chatPanelId) {
            this.chatPanelId = chatPanelId;
        }

        BasePluginMessageResult processEnterRoom(final PluginMessage msg) {
            BasePluginMessageResult result;
            final ChatEnterRoomMessage cerMsg = (ChatEnterRoomMessage) msg;
            logger.debug("looking up chat panel " + getChatPanelId());
            Element chatPanel = nifty.getCurrentScreen().findElementByName(CHAT);
            final Chat chat = chatPanel.findNiftyControl(getChatPanelId(), Chat.class);
            logger.debug("adding player " + cerMsg.getAccount().getName());
            logger.debug("with avatar filename " + cerMsg.getAccount().getAvatar().getFileName());
            NiftyImage icon = null;
            if (cerMsg.getAccount().getAvatar().getFileName() == null) {
                icon = nifty.getRenderEngine().createImage(ICON_HOURGLASS, false);
                logger.debug("set avatar filename to " + cerMsg.getAccount().getAvatar().getFileName());
                Thread thread = new Thread(new Runnable() {

                    private AccountVO account = cerMsg.getAccount();

                    @Override
                    public void run() {
                        String avatarFileName = null;
                        boolean stop = false;
                        AvatarVO avatar = new AvatarVO();
                        avatar.setId(account.getAvatar().getId());
                        avatar.setVersion(account.getAvatar().getVersion());
                        avatar.setType(account.getAvatar().getType());
                        final GetAvatarMessage gaMsg = new GetAvatarMessage();
                        gaMsg.setAvatar(avatar);
                        try {
                            final GetAvatarMessageResult gaResult = (GetAvatarMessageResult) PluginController.getCurrentPluginController().processMessage(
                                    "accountPlugin", gaMsg);
                            if (gaResult.getErrorCode() == MessageResultsErrorCodes.CACHED_FILE_NOT_FOUND) {
                                logger.info("file not cached, downloading.");
                            } else if (gaResult.getErrorCode() != MessageResultsErrorCodes.SUCCESS) {
                                logger.warn("Unable to load the avatar image for user " + cerMsg.getAccount().getId() + ", errorcode: " + gaResult.getErrorCode());
                                stop = true;
                            }
                            avatarFileName = gaResult.getAvatar().getFileName();
                        }
                        catch (PluginException ex) {
                            logger.warn("Unable to load the avatar image for user " + cerMsg.getAccount().getId(), ex);
                            stop = true;
                        }

                        while (!stop) {
                            File avatarFile = new File(avatarFileName);
                            if (avatarFile.exists()) {
                                cerMsg.getAccount().getAvatar().setFileName(avatarFileName);
                                reloadAvatarImage(cerMsg.getAccount());
                                stop = true;
                            }
                            try {
                                Thread.sleep(1000);
                            }
                            catch (InterruptedException ex) {
                            }
                        }
                    }
                });
                thread.start();
            } else {
                icon = nifty.getRenderEngine().createImage(cerMsg.getAccount().getAvatar().getFileName(), false);
            }

            logger.debug("creating NiftyImage from file " + cerMsg.getAccount().getAvatar().getFileName());
            if (cerMsg.isCurrentPlayer()) {
                chat.addPlayer(cerMsg.getAccount().getName(), icon, "current-player");
            } else {
                chat.addPlayer(cerMsg.getAccount().getName(), icon);
            }
            result = new DisplayScreenMessageResult();
            result.setErrorCode(MessageResultsErrorCodes.SUCCESS);
            return result;
        }

        private void reloadAvatarImage(AccountVO account) {
            // first, loop through all the players in the members list.
            logger.debug("looking up chat panel " + getChatPanelId());
            Element chatPanel = nifty.getCurrentScreen().findElementByName(CHAT);
            final Chat chat = chatPanel.findNiftyControl(getChatPanelId(), Chat.class);
            List<ChatEntryModelClass> players = chat.getPlayers();
            List<ChatEntryModelClass> lines = chat.getLines();
            for (ChatEntryModelClass player : players) {
                if (account.getName().equals(player.getLabel())) {
                    // in this case we have the current account linked to the 
                    // current player and we reload the image.
                    logger.debug("replacing the hourglass with " + account.getAvatar().getFileName());
                    player.setIcon(nifty.getRenderEngine().createImage(
                            account.getAvatar().getFileName(), false));
                }
            }
            for (ChatEntryModelClass line : lines) {
                if (line.getLabel().startsWith(account.getName())) {
                    // in this case, the line is linked to the current account 
                    // and we reload the image
                    line.setIcon(nifty.getRenderEngine().createImage(
                            account.getAvatar().getFileName(), false));
                }
            }
            chat.update();
        }

        BasePluginMessageResult processLeaveRoom(final PluginMessage msg) {
            BasePluginMessageResult result;
            final ChatLeaveRoomMessage clrMsg = (ChatLeaveRoomMessage) msg;
            logger.debug("looking up chat panel " + getChatPanelId());
            Element chatPanel = nifty.getCurrentScreen().findElementByName(CHAT);
            final Chat chat = chatPanel.findNiftyControl(getChatPanelId(), Chat.class);
            chat.removePlayer(clrMsg.getAccount().getName());
            result = new DisplayScreenMessageResult();
            result.setErrorCode(MessageResultsErrorCodes.SUCCESS);
            return result;
        }

        BasePluginMessageResult processChatLine(final PluginMessage msg) {
            BasePluginMessageResult result;
            final ChatLineMessage clMsg = (ChatLineMessage) msg;
            if (clMsg.getAccount().getAvatar().getFileName() == null) {
                clMsg.getAccount().getAvatar().setFileName(ICON_HOURGLASS);
            }
            logger.debug("looking up chat panel " + getChatPanelId());
            Element chatPanel = nifty.getCurrentScreen().findElementByName(CHAT);
            final Chat chatController = chatPanel.findElementByName(getChatPanelId()).getControl(ChatControl.class);
            if (clMsg.isCurrentPlayer()) {
                chatController.receivedChatLine(clMsg.getAccount().getName() + NAME_LINE_SEPERATOR + clMsg.getLine(),
                        nifty.getRenderEngine().createImage(clMsg.getAccount().getAvatar().getFileName(), false), "current-player");
            } else {
                chatController.receivedChatLine(clMsg.getAccount().getName() + NAME_LINE_SEPERATOR + clMsg.getLine(),
                        nifty.getRenderEngine().createImage(clMsg.getAccount().getAvatar().getFileName(), false));
            }
            result = new DisplayScreenMessageResult();
            result.setErrorCode(MessageResultsErrorCodes.SUCCESS);
            return result;
        }
    }
}
